// --------------------------------------------------------------------------------------------------------------------
// <copyright file="MemUtil.cs" company="">
//   
// </copyright>
// <summary>
//   Contains static buffer manipulation and string conversion routines.
// </summary>
// --------------------------------------------------------------------------------------------------------------------


using System.IO.Compression;
#if !KeePassLibSD

#else
using KeePassLibSD;
#endif

namespace KeePassLib.Utility
{
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.IO;
    using System.Text;

    /// <summary>
    /// Contains static buffer manipulation and string conversion routines.
    /// </summary>
    public static class MemUtil
    {
        #region Static Fields

        /// <summary>
        /// The m_v s box.
        /// </summary>
        private static readonly uint[] m_vSBox = new uint[256]
                                                     {
                                                         0xCD2FACB3, 0xE78A7F5C, 0x6F0803FC, 0xBCF6E230, 0x3A321712, 0x06403DB1, 0xD2F84B95, 0xDF22A6E4, 
                                                         0x07CE9E5B, 0x31788A0C, 0xF683F6F4, 0xEA061F49, 0xFA5C2ACA, 0x4B9E494E, 0xB0AB25BA, 0x767731FC, 
                                                         0x261893A7, 0x2B09F2CE, 0x046261E4, 0x41367B4B, 0x18A7F225, 0x8F923C0E, 0x5EF3A325, 0x28D0435E, 
                                                         0x84C22919, 0xED66873C, 0x8CEDE444, 0x7FC47C24, 0xFCFC6BA3, 0x676F928D, 0xB4147187, 0xD8FB126E, 
                                                         0x7D798D17, 0xFF82E424, 0x1712FA5B, 0xABB09DD5, 0x8156BA63, 0x84E4D969, 0xC937FB9A, 0x2F1E5BFC, 
                                                         0x178ECA11, 0x0E71CD5F, 0x52AAC6F4, 0x71EEFC8F, 0x7090D749, 0x21CACA31, 0x92996378, 0x0939A8A8, 
                                                         0xE9EE1934, 0xD2718616, 0xF2500543, 0xB911873C, 0xD3CB3EEC, 0x2BA0DBEB, 0xB42D0A27, 0xECE67C0F, 
                                                         0x302925F0, 0x6114F839, 0xD39E6307, 0xE28970D6, 0xEB982F99, 0x941B4CDF, 0xC540E550, 0x8124FC45, 
                                                         0x98B025C7, 0xE2BF90EA, 0x4F57C976, 0xCF546FE4, 0x59566DC8, 0xE3F4360D, 0xF5F9D231, 0xD6180B22, 
                                                         0xB54E088A, 0xB5DFE6A6, 0x3637A36F, 0x056E9284, 0xAFF8FBC5, 0x19E01648, 0x8611F043, 0xDAE44337, 
                                                         0xF61B6A1C, 0x257ACD9E, 0xDD35F507, 0xEF05CAFA, 0x05EB4A83, 0xFC25CA92, 0x0A4728E6, 0x9CF150EF, 
                                                         0xAEEF67DE, 0xA9472337, 0x57C81EFE, 0x3E5E009F, 0x02CB03BB, 0x2BA85674, 0xF21DC251, 0x78C34A34, 
                                                         0xABB1F5BF, 0xB95A2FBD, 0x1FB47777, 0x9A96E8AC, 0x5D2D2838, 0x55AAC92A, 0x99EE324E, 0x10F6214B, 
                                                         0x58ABDFB1, 0x2008794D, 0xBEC880F0, 0xE75E5341, 0x88015C34, 0x352D8FBF, 0x622B7F6C, 0xF5C59EA2, 
                                                         0x1F759D8E, 0xADE56159, 0xCC7B4C25, 0x5B8BC48C, 0xB6BD15AF, 0x3C5B5110, 0xE74A7C3D, 0xEE613161, 
                                                         0x156A1C67, 0x72C06817, 0xEA0A6F69, 0x4CECF993, 0xCA9D554C, 0x8E20361F, 0x42D396B9, 0x595DE578, 
                                                         0x749D7955, 0xFD1BA5FD, 0x81FC160E, 0xDB97E28C, 0x7CF148F7, 0x0B0B3CF5, 0x534DE605, 0x46421066, 
                                                         0xD4B68DD1, 0x9E479CE6, 0xAE667A9D, 0xBC082082, 0xB06DD6EF, 0x20F0F23F, 0xB99E1551, 0xF47A2E3A, 
                                                         0x71DA50C6, 0x67B65779, 0x2A8CB376, 0x1EA71EEE, 0x29ABCD50, 0xB6EB0C6B, 0x23C10511, 0x6F3F2144, 
                                                         0x6AF23012, 0xF696BD9E, 0xB94099D8, 0xAD5A9C81, 0x7A0794FA, 0x7EDF59D6, 0x1E72E574, 0x8561913C, 
                                                         0x4E4D568F, 0xEECB9928, 0x9C124D2E, 0x0848B82C, 0xF1CA395F, 0x9DAF43DC, 0xF77EC323, 0x394E9B59, 
                                                         0x7E200946, 0x8B811D68, 0x16DA3305, 0xAB8DE2C3, 0xE6C53B64, 0x98C2D321, 0x88A97D81, 0xA7106419, 
                                                         0x8E52F7BF, 0x8ED262AF, 0x7CCA974E, 0xF0933241, 0x040DD437, 0xE143B3D4, 0x3019F56F, 0xB741521D, 
                                                         0xF1745362, 0x4C435F9F, 0xB4214D0D, 0x0B0C348B, 0x5051D189, 0x4C30447E, 0x7393D722, 0x95CEDD0B, 
                                                         0xDD994E80, 0xC3D22ED9, 0x739CD900, 0x131EB9C4, 0xEF1062B2, 0x4F0DE436, 0x52920073, 0x9A7F3D80, 
                                                         0x896E7B1B, 0x2C8BBE5A, 0xBD304F8A, 0xA993E22C, 0x134C41A0, 0xFA989E00, 0x39CE9726, 0xFB89FCCF, 
                                                         0xE8FBAC97, 0xD4063FFC, 0x935A2B5A, 0x44C8EE83, 0xCB2BC7B6, 0x02989E92, 0x75478BEA, 0x144378D0, 
                                                         0xD853C087, 0x8897A34E, 0xDD23629D, 0xBDE2A2A2, 0x581D8ECC, 0x5DA8AEE8, 0xFF8AAFD0, 0xBA2BCF6E, 
                                                         0x4BD98DAC, 0xF2EDB9E4, 0xFA2DC868, 0x47E84661, 0xECEB1C7D, 0x41705CA4, 0x5982E4D4, 0xEB5204A1, 
                                                         0xD196CAFB, 0x6414804D, 0x3ABD4B46, 0x8B494C26, 0xB432D52B, 0x39C5356B, 0x6EC80BF7, 0x71BE5483, 
                                                         0xCEC4A509, 0xE9411D61, 0x52F341E5, 0xD2E6197B, 0x4F02826C, 0xA9E48838, 0xD1F8F247, 0xE4957FB3, 
                                                         0x586CCA99, 0x9A8B6A5B, 0x4998FBEA, 0xF762BE4C, 0x90DFE33C, 0x9731511E, 0x88C6A82F, 0xDD65A4D4
                                                     };

        #endregion

        #region Public Methods and Operators

        /// <summary>
        /// The arrays equal.
        /// </summary>
        /// <param name="x">
        /// The x.
        /// </param>
        /// <param name="y">
        /// The y.
        /// </param>
        /// <returns>
        /// The <see cref="bool"/>.
        /// </returns>
        public static bool ArraysEqual(byte[] x, byte[] y)
        {
            // Return false if one of them is null (not comparable)!
            if ((x == null) || (y == null))
            {
                Debug.Assert(false);
                return false;
            }

            if (x.Length != y.Length)
            {
                return false;
            }

            for (int i = 0; i < x.Length; ++i)
            {
                if (x[i] != y[i])
                {
                    return false;
                }
            }

            return true;
        }

        /// <summary>
        /// Convert a byte array to a hexadecimal string.
        /// </summary>
        /// <param name="pbArray">
        /// Input byte array.
        /// </param>
        /// <returns>
        /// Returns the hexadecimal string representing the byte
        /// array. Returns <c>null</c>, if the input byte array was <c>null</c>. Returns
        /// an empty string, if the input byte array has length 0.
        /// </returns>
        public static string ByteArrayToHexString(byte[] pbArray)
        {
            if (pbArray == null)
            {
                return null;
            }

            int nLen = pbArray.Length;
            if (nLen == 0)
            {
                return string.Empty;
            }

            StringBuilder sb = new StringBuilder();

            byte bt, btHigh, btLow;
            for (int i = 0; i < nLen; ++i)
            {
                bt = pbArray[i];
                btHigh = bt;
                btHigh >>= 4;
                btLow = (byte)(bt & 0x0F);

                if (btHigh >= 10)
                {
                    sb.Append((char)('A' + btHigh - 10));
                }
                else
                {
                    sb.Append((char)('0' + btHigh));
                }

                if (btLow >= 10)
                {
                    sb.Append((char)('A' + btLow - 10));
                }
                else
                {
                    sb.Append((char)('0' + btLow));
                }
            }

            return sb.ToString();
        }

        /// <summary>
        /// Convert 2 bytes to a 16-bit unsigned integer using Little-Endian
        /// encoding.
        /// </summary>
        /// <param name="pb">
        /// Input bytes. Array must contain at least 2 bytes.
        /// </param>
        /// <returns>
        /// 16-bit unsigned integer.
        /// </returns>
        public static ushort BytesToUInt16(byte[] pb)
        {
            Debug.Assert((pb != null) && (pb.Length == 2));
            if (pb == null)
            {
                throw new ArgumentNullException("pb");
            }

            if (pb.Length != 2)
            {
                throw new ArgumentException();
            }

            return (ushort)((ushort)pb[0] | ((ushort)pb[1] << 8));
        }

        /// <summary>
        /// Convert 4 bytes to a 32-bit unsigned integer using Little-Endian
        /// encoding.
        /// </summary>
        /// <param name="pb">
        /// Input bytes.
        /// </param>
        /// <returns>
        /// 32-bit unsigned integer.
        /// </returns>
        public static uint BytesToUInt32(byte[] pb)
        {
            Debug.Assert((pb != null) && (pb.Length == 4));
            if (pb == null)
            {
                throw new ArgumentNullException("pb");
            }

            if (pb.Length != 4)
            {
                throw new ArgumentException("Input array must contain 4 bytes!");
            }

            return (uint)pb[0] | ((uint)pb[1] << 8) | ((uint)pb[2] << 16) | ((uint)pb[3] << 24);
        }

        /// <summary>
        /// Convert 8 bytes to a 64-bit unsigned integer using Little-Endian
        /// encoding.
        /// </summary>
        /// <param name="pb">
        /// Input bytes.
        /// </param>
        /// <returns>
        /// 64-bit unsigned integer.
        /// </returns>
        public static ulong BytesToUInt64(byte[] pb)
        {
            Debug.Assert((pb != null) && (pb.Length == 8));
            if (pb == null)
            {
                throw new ArgumentNullException("pb");
            }

            if (pb.Length != 8)
            {
                throw new ArgumentException();
            }

            return (ulong)pb[0] | ((ulong)pb[1] << 8) | ((ulong)pb[2] << 16) | ((ulong)pb[3] << 24) | ((ulong)pb[4] << 32) | ((ulong)pb[5] << 40)
                   | ((ulong)pb[6] << 48) | ((ulong)pb[7] << 56);
        }

        /// <summary>
        /// The compress.
        /// </summary>
        /// <param name="pbData">
        /// The pb data.
        /// </param>
        /// <returns>
        /// The <see cref="byte[]"/>.
        /// </returns>
        /// <exception cref="ArgumentNullException">
        /// </exception>
        public static byte[] Compress(byte[] pbData)
        {
            if (pbData == null)
            {
                throw new ArgumentNullException("pbData");
            }

            if (pbData.Length == 0)
            {
                return pbData;
            }

            using (MemoryStream msCompressed = new MemoryStream())
            {
                using (GZipStream gz = new GZipStream(msCompressed, CompressionMode.Compress))
                using (MemoryStream msSource = new MemoryStream(pbData, false))
                {
                    MemUtil.CopyStream(msSource, gz);
                }

                byte[] pbCompressed = msCompressed.ToArray();
                return pbCompressed;
            }
        }

        /// <summary>
        /// The copy stream.
        /// </summary>
        /// <param name="sSource">
        /// The s source.
        /// </param>
        /// <param name="sTarget">
        /// The s target.
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// </exception>
        public static void CopyStream(Stream sSource, Stream sTarget)
        {
            Debug.Assert((sSource != null) && (sTarget != null));
            if (sSource == null)
            {
                throw new ArgumentNullException("sSource");
            }

            if (sTarget == null)
            {
                throw new ArgumentNullException("sTarget");
            }

            const int nBufSize = 4096;
            byte[] pbBuf = new byte[nBufSize];

            while (true)
            {
                int nRead = sSource.Read(pbBuf, 0, nBufSize);
                if (nRead == 0)
                {
                    break;
                }

                sTarget.Write(pbBuf, 0, nRead);
            }

            // Do not close any of the streams
        }

        /// <summary>
        /// The decompress.
        /// </summary>
        /// <param name="pbCompressed">
        /// The pb compressed.
        /// </param>
        /// <returns>
        /// The <see cref="byte[]"/>.
        /// </returns>
        /// <exception cref="ArgumentNullException">
        /// </exception>
        public static byte[] Decompress(byte[] pbCompressed)
        {
            if (pbCompressed == null)
            {
                throw new ArgumentNullException("pbCompressed");
            }

            if (pbCompressed.Length == 0)
            {
                return pbCompressed;
            }

            using (MemoryStream msCompressed = new MemoryStream(pbCompressed, false))
            using (GZipStream gz = new GZipStream(msCompressed, CompressionMode.Decompress))
            using (MemoryStream msData = new MemoryStream())
            {
                MemUtil.CopyStream(gz, msData);
                byte[] pbData = msData.ToArray();
                return pbData;
            }
        }

        /// <summary>
        /// The except.
        /// </summary>
        /// <param name="a">
        /// The a.
        /// </param>
        /// <param name="b">
        /// The b.
        /// </param>
        /// <param name="cmp">
        /// The cmp.
        /// </param>
        /// <typeparam name="T">
        /// </typeparam>
        /// <returns>
        /// The <see cref="IEnumerable"/>.
        /// </returns>
        /// <exception cref="ArgumentNullException">
        /// </exception>
        public static IEnumerable<T> Except<T>(IEnumerable<T> a, IEnumerable<T> b, IEqualityComparer<T> cmp)
        {
            if (a == null)
            {
                throw new ArgumentNullException("a");
            }

            if (b == null)
            {
                throw new ArgumentNullException("b");
            }

            Dictionary<T, bool> d = (cmp != null) ? (new Dictionary<T, bool>(cmp)) : (new Dictionary<T, bool>());

            foreach (T tb in b)
            {
                d[tb] = true;
            }

            foreach (T ta in a)
            {
                if (d.ContainsKey(ta))
                {
                    continue;
                }

                d[ta] = true; // Prevent duplicates
                yield return ta;
            }

            yield break;
        }

        /// <summary>
        /// Fast hash that can be used e.g. for hash tables.
        /// The algorithm might change in the future; do not store
        /// the hashes for later use.
        /// </summary>
        /// <param name="v">
        /// The v.
        /// </param>
        /// <param name="iStart">
        /// The i Start.
        /// </param>
        /// <param name="iLength">
        /// The i Length.
        /// </param>
        /// <returns>
        /// The <see cref="uint"/>.
        /// </returns>
        public static uint Hash32(byte[] v, int iStart, int iLength)
        {
            uint u = 0x326F637B;

            if (v == null)
            {
                Debug.Assert(false);
                return u;
            }

            if (iStart < 0)
            {
                Debug.Assert(false);
                return u;
            }

            if (iLength < 0)
            {
                Debug.Assert(false);
                return u;
            }

            int m = iStart + iLength;
            if (m > v.Length)
            {
                Debug.Assert(false);
                return u;
            }

            for (int i = iStart; i < m; ++i)
            {
                u ^= m_vSBox[v[i]];
                u *= 3;
            }

            return u;
        }

        /// <summary>
        /// Convert a hexadecimal string to a byte array. The input string must be
        /// even (i.e. its length is a multiple of 2).
        /// </summary>
        /// <param name="strHex">
        /// String containing hexadecimal characters.
        /// </param>
        /// <returns>
        /// Returns a byte array. Returns <c>null</c> if the string parameter
        /// was <c>null</c> or is an uneven string (i.e. if its length isn't a
        /// multiple of 2).
        /// </returns>
        /// <exception cref="System.ArgumentNullException">
        /// Thrown if <paramref name="strHex"/>
        /// is <c>null</c>.
        /// </exception>
        public static byte[] HexStringToByteArray(string strHex)
        {
            if (strHex == null)
            {
                Debug.Assert(false);
                throw new ArgumentNullException("strHex");
            }

            int nStrLen = strHex.Length;
            if ((nStrLen & 1) != 0)
            {
                Debug.Assert(false);
                return null;
            }

            byte[] pb = new byte[nStrLen / 2];
            byte bt;
            char ch;

            for (int i = 0; i < nStrLen; i += 2)
            {
                ch = strHex[i];

                if ((ch >= '0') && (ch <= '9'))
                {
                    bt = (byte)(ch - '0');
                }
                else if ((ch >= 'a') && (ch <= 'f'))
                {
                    bt = (byte)(ch - 'a' + 10);
                }
                else if ((ch >= 'A') && (ch <= 'F'))
                {
                    bt = (byte)(ch - 'A' + 10);
                }
                else
                {
                    Debug.Assert(false);
                    bt = 0;
                }

                bt <<= 4;

                ch = strHex[i + 1];
                if ((ch >= '0') && (ch <= '9'))
                {
                    bt += (byte)(ch - '0');
                }
                else if ((ch >= 'a') && (ch <= 'f'))
                {
                    bt += (byte)(ch - 'a' + 10);
                }
                else if ((ch >= 'A') && (ch <= 'F'))
                {
                    bt += (byte)(ch - 'A' + 10);
                }
                else
                {
                    Debug.Assert(false);
                }

                pb[i >> 1] = bt;
            }

            return pb;
        }

        /// <summary>
        /// The index of.
        /// </summary>
        /// <param name="vHaystack">
        /// The v haystack.
        /// </param>
        /// <param name="vNeedle">
        /// The v needle.
        /// </param>
        /// <typeparam name="T">
        /// </typeparam>
        /// <returns>
        /// The <see cref="int"/>.
        /// </returns>
        /// <exception cref="ArgumentNullException">
        /// </exception>
        public static int IndexOf<T>(T[] vHaystack, T[] vNeedle) where T : IEquatable<T>
        {
            if (vHaystack == null)
            {
                throw new ArgumentNullException("vHaystack");
            }

            if (vNeedle == null)
            {
                throw new ArgumentNullException("vNeedle");
            }

            if (vNeedle.Length == 0)
            {
                return 0;
            }

            for (int i = 0; i <= (vHaystack.Length - vNeedle.Length); ++i)
            {
                bool bFound = true;
                for (int m = 0; m < vNeedle.Length; ++m)
                {
                    if (!vHaystack[i + m].Equals(vNeedle[m]))
                    {
                        bFound = false;
                        break;
                    }
                }

                if (bFound)
                {
                    return i;
                }
            }

            return -1;
        }

        /// <summary>
        /// The intersect.
        /// </summary>
        /// <param name="a">
        /// The a.
        /// </param>
        /// <param name="b">
        /// The b.
        /// </param>
        /// <param name="cmp">
        /// The cmp.
        /// </param>
        /// <typeparam name="T">
        /// </typeparam>
        /// <returns>
        /// The <see cref="IEnumerable"/>.
        /// </returns>
        /// <exception cref="ArgumentNullException">
        /// </exception>
        public static IEnumerable<T> Intersect<T>(IEnumerable<T> a, IEnumerable<T> b, IEqualityComparer<T> cmp)
        {
            if (a == null)
            {
                throw new ArgumentNullException("a");
            }

            if (b == null)
            {
                throw new ArgumentNullException("b");
            }

            Dictionary<T, bool> d = (cmp != null) ? (new Dictionary<T, bool>(cmp)) : (new Dictionary<T, bool>());

            foreach (T tb in b)
            {
                d[tb] = true;
            }

            foreach (T ta in a)
            {
                if (d.Remove(ta))
                {
                    // Prevent duplicates
                    yield return ta;
                }
            }

            yield break;
        }

        /// <summary>
        /// The mid.
        /// </summary>
        /// <param name="v">
        /// The v.
        /// </param>
        /// <param name="iOffset">
        /// The i offset.
        /// </param>
        /// <param name="iLength">
        /// The i length.
        /// </param>
        /// <typeparam name="T">
        /// </typeparam>
        /// <returns>
        /// The <see cref="T[]"/>.
        /// </returns>
        /// <exception cref="ArgumentNullException">
        /// </exception>
        /// <exception cref="ArgumentOutOfRangeException">
        /// </exception>
        /// <exception cref="ArgumentException">
        /// </exception>
        public static T[] Mid<T>(T[] v, int iOffset, int iLength)
        {
            if (v == null)
            {
                throw new ArgumentNullException("v");
            }

            if (iOffset < 0)
            {
                throw new ArgumentOutOfRangeException("iOffset");
            }

            if (iLength < 0)
            {
                throw new ArgumentOutOfRangeException("iLength");
            }

            if ((iOffset + iLength) > v.Length)
            {
                throw new ArgumentException();
            }

            T[] r = new T[iLength];
            Array.Copy(v, iOffset, r, 0, iLength);
            return r;
        }

        /// <summary>
        /// The read.
        /// </summary>
        /// <param name="s">
        /// The s.
        /// </param>
        /// <param name="nCount">
        /// The n count.
        /// </param>
        /// <returns>
        /// The <see cref="byte[]"/>.
        /// </returns>
        /// <exception cref="ArgumentNullException">
        /// </exception>
        /// <exception cref="ArgumentOutOfRangeException">
        /// </exception>
        public static byte[] Read(Stream s, int nCount)
        {
            if (s == null)
            {
                throw new ArgumentNullException("s");
            }

            if (nCount < 0)
            {
                throw new ArgumentOutOfRangeException("nCount");
            }

            byte[] pb = new byte[nCount];
            int iOffset = 0;
            while (nCount > 0)
            {
                int iRead = s.Read(pb, iOffset, nCount);
                if (iRead == 0)
                {
                    break;
                }

                iOffset += iRead;
                nCount -= iRead;
            }

            if (iOffset != pb.Length)
            {
                byte[] pbPart = new byte[iOffset];
                Array.Copy(pb, pbPart, iOffset);
                return pbPart;
            }

            return pb;
        }

        /// <summary>
        /// Convert a 16-bit unsigned integer to 2 bytes using Little-Endian
        /// encoding.
        /// </summary>
        /// <param name="uValue">
        /// 16-bit input word.
        /// </param>
        /// <returns>
        /// Two bytes representing the 16-bit value.
        /// </returns>
        public static byte[] UInt16ToBytes(ushort uValue)
        {
            byte[] pb = new byte[2];

            unchecked
            {
                pb[0] = (byte)uValue;
                pb[1] = (byte)(uValue >> 8);
            }

            return pb;
        }

        /// <summary>
        /// Convert a 32-bit unsigned integer to 4 bytes using Little-Endian
        /// encoding.
        /// </summary>
        /// <param name="uValue">
        /// 32-bit input word.
        /// </param>
        /// <returns>
        /// Four bytes representing the 32-bit value.
        /// </returns>
        public static byte[] UInt32ToBytes(uint uValue)
        {
            byte[] pb = new byte[4];

            unchecked
            {
                pb[0] = (byte)uValue;
                pb[1] = (byte)(uValue >> 8);
                pb[2] = (byte)(uValue >> 16);
                pb[3] = (byte)(uValue >> 24);
            }

            return pb;
        }

        /// <summary>
        /// Convert a 64-bit unsigned integer to 8 bytes using Little-Endian
        /// encoding.
        /// </summary>
        /// <param name="uValue">
        /// 64-bit input word.
        /// </param>
        /// <returns>
        /// Eight bytes representing the 64-bit value.
        /// </returns>
        public static byte[] UInt64ToBytes(ulong uValue)
        {
            byte[] pb = new byte[8];

            unchecked
            {
                pb[0] = (byte)uValue;
                pb[1] = (byte)(uValue >> 8);
                pb[2] = (byte)(uValue >> 16);
                pb[3] = (byte)(uValue >> 24);
                pb[4] = (byte)(uValue >> 32);
                pb[5] = (byte)(uValue >> 40);
                pb[6] = (byte)(uValue >> 48);
                pb[7] = (byte)(uValue >> 56);
            }

            return pb;
        }

        /// <summary>
        /// The union.
        /// </summary>
        /// <param name="a">
        /// The a.
        /// </param>
        /// <param name="b">
        /// The b.
        /// </param>
        /// <param name="cmp">
        /// The cmp.
        /// </param>
        /// <typeparam name="T">
        /// </typeparam>
        /// <returns>
        /// The <see cref="IEnumerable"/>.
        /// </returns>
        /// <exception cref="ArgumentNullException">
        /// </exception>
        public static IEnumerable<T> Union<T>(IEnumerable<T> a, IEnumerable<T> b, IEqualityComparer<T> cmp)
        {
            if (a == null)
            {
                throw new ArgumentNullException("a");
            }

            if (b == null)
            {
                throw new ArgumentNullException("b");
            }

            Dictionary<T, bool> d = (cmp != null) ? (new Dictionary<T, bool>(cmp)) : (new Dictionary<T, bool>());

            foreach (T ta in a)
            {
                if (d.ContainsKey(ta))
                {
                    continue; // Prevent duplicates
                }

                d[ta] = true;
                yield return ta;
            }

            foreach (T tb in b)
            {
                if (d.ContainsKey(tb))
                {
                    continue; // Prevent duplicates
                }

                d[tb] = true;
                yield return tb;
            }

            yield break;
        }

        /// <summary>
        /// The write.
        /// </summary>
        /// <param name="s">
        /// The s.
        /// </param>
        /// <param name="pbData">
        /// The pb data.
        /// </param>
        public static void Write(Stream s, byte[] pbData)
        {
            if (s == null)
            {
                Debug.Assert(false);
                return;
            }

            if (pbData == null)
            {
                Debug.Assert(false);
                return;
            }

            s.Write(pbData, 0, pbData.Length);
        }

        /// <summary>
        /// The xor array.
        /// </summary>
        /// <param name="pbSource">
        /// The pb source.
        /// </param>
        /// <param name="nSourceOffset">
        /// The n source offset.
        /// </param>
        /// <param name="pbBuffer">
        /// The pb buffer.
        /// </param>
        /// <param name="nBufferOffset">
        /// The n buffer offset.
        /// </param>
        /// <param name="nLength">
        /// The n length.
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// </exception>
        /// <exception cref="ArgumentException">
        /// </exception>
        public static void XorArray(byte[] pbSource, int nSourceOffset, byte[] pbBuffer, int nBufferOffset, int nLength)
        {
            if (pbSource == null)
            {
                throw new ArgumentNullException("pbSource");
            }

            if (nSourceOffset < 0)
            {
                throw new ArgumentException();
            }

            if (pbBuffer == null)
            {
                throw new ArgumentNullException("pbBuffer");
            }

            if (nBufferOffset < 0)
            {
                throw new ArgumentException();
            }

            if (nLength < 0)
            {
                throw new ArgumentException();
            }

            if ((nSourceOffset + nLength) > pbSource.Length)
            {
                throw new ArgumentException();
            }

            if ((nBufferOffset + nLength) > pbBuffer.Length)
            {
                throw new ArgumentException();
            }

            for (int i = 0; i < nLength; ++i)
            {
                pbBuffer[nBufferOffset + i] ^= pbSource[nSourceOffset + i];
            }
        }

        /// <summary>
        /// Set all bytes in a byte array to zero.
        /// </summary>
        /// <param name="pbArray">
        /// Input array. All bytes of this array will be set
        /// to zero.
        /// </param>
        public static void ZeroByteArray(byte[] pbArray)
        {
            Debug.Assert(pbArray != null);
            if (pbArray == null)
            {
                throw new ArgumentNullException("pbArray");
            }

            // for(int i = 0; i < pbArray.Length; ++i)
            // 	pbArray[i] = 0;
            Array.Clear(pbArray, 0, pbArray.Length);
        }

        #endregion
    }
}